/*
******************************************************************************************************* 
**  Copyright (C) 2019, 苏州检易生物科技有限公司 
**  All rights reserved. 
** 
**  FileName:       	spi_flash.c
**  Description:	
**  Author:        	 
**  Version				v0.1
**  Date:           	2019-06-21
**	Function List： 
**  History:         
*********************************************************************************************************
*/


/*
*********************************************************************************************************
*                                              	调试开关
*********************************************************************************************************
*/


/*
*********************************************************************************************************
*                                             INCLUDE FILES
*********************************************************************************************************
*/
#include "spi_flash.h"
#include "log_console.h"
#ifndef BOOTLOADER
#include <rtthread.h>
#endif


/*
*********************************************************************************************************
*                                              	宏定义
*********************************************************************************************************
*/
#define SPI_FLASH_CMD_SIZE		16
#define SPI_DMA_TIMEOUT				5000
/*
*********************************************************************************************************
*                                              	结构体定义
*********************************************************************************************************
*/

 
/*
*********************************************************************************************************
*                                              	函数原型声明
*********************************************************************************************************
*/
/*因为flash write函数使用了read和erase 对于flash访问的互斥不太好写，这里再包一层*/
static flash_err_t spi_flash_read(w25q128_t*,uint32_t addr,uint8_t *buf,uint16_t size);

static flash_err_t spi_flash_erase(w25q128_t*,uint32_t addr,uint32_t size);

static uint32_t flash_read_id(w25q128_t*);

static void flash_send_cmd(w25q128_t*,uint8_t cmd);

static flash_err_t flash_sector_erase(w25q128_t*,uint32_t addr);

static void flash_write_enable(w25q128_t*);

static void flash_write_disable(w25q128_t*);

static flash_err_t flash_page_write(w25q128_t*,uint32_t addr,uint8_t *buf,uint16_t size);

static void flash_wait_busy(w25q128_t*);

static void wait_write_enable(w25q128_t*);

static void wait_write_disable(w25q128_t*);

static flash_err_t flash_sector_write(w25q128_t*,uint32_t addr,uint8_t *buf,uint16_t size);

static void spi_dma_cplt_callback(spi_handle_t * ,uint8_t * rxbuff, uint16_t size);
/*
*********************************************************************************************************
*                                              	全局变量定义
*********************************************************************************************************
*/


/*
*********************************************************************************************************
*                                              	模块静态变量定义
*********************************************************************************************************
*/

w25q128_t w25q128_handle={
	.spi_chn=W25Q128_SPI_CHANNEL,
	.cs_pin_base=W25Q128_SPI_CS_GPIO_BASE,
	.cs_pin=W25Q128_SPI_CS_GPIO_PIN,
	.status=FLAHS_UNINIT,
};
static uint8_t flash_cache[W25X_SECTOR_SIZE];
/*
*********************************************************************************************************
*                                              	函数定义
*********************************************************************************************************
*/

/*******************************************************************************
 *
 * @brief    flash_init    初始化flash，包括初始化与flash通信的spi控制器；读取flash
 *                         第一个扇区，判断是否初次使用，初次使用需要擦除整个块
*******************************************************************************/
w25q128_t* flash_open(void)
{
	if(w25q128_handle.status != FLAHS_UNINIT)
		return &w25q128_handle;
	
	w25q128_handle.spi_handle=spi_open(w25q128_handle.spi_chn);
#if SPI_FLASH_ASYN
//	spi_dma_init(w25q128_handle.spi_handle,spi_dma_cplt_callback);
	
	if(RT_EOK != rt_sem_init(&w25q128_handle.w25q_sem,"w25q_sem",0,RT_IPC_FLAG_PRIO))
	{
		Critical_Log("W25Q sem create failed!\r\n");
	}
//	if(RT_EOK != rt_sem_init(&w25q128_handle.dma_sem,"dma_sem",0,RT_IPC_FLAG_PRIO))
//	{
//		Critical_Log("W25Q dma_sem create failed!\r\n");
//	}
#endif
	
	GPIO_InitTypeDef  GPIO_InitStruct;
	GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_HIGH;
	GPIO_InitStruct.Pull=GPIO_PULLUP;
	__HAL_RCC_GPIOF_CLK_ENABLE();
	GPIO_InitStruct.Pin=W25Q128_SPI_CS_GPIO_PIN;
	HAL_GPIO_Init(W25Q128_SPI_CS_GPIO_BASE,&GPIO_InitStruct);
	__W25Q_CS_DESELECT((&w25q128_handle));
	
	GPIO_InitStruct.Pin=W25Q128_SPI_WP_GPIO_PIN;
	HAL_GPIO_Init(W25Q128_SPI_WP_GPIO_BASE,&GPIO_InitStruct);
	HAL_GPIO_WritePin(W25Q128_SPI_WP_GPIO_BASE,W25Q128_SPI_WP_GPIO_PIN,GPIO_PIN_SET);
	
	uint32_t flash_id = (flash_read_id(&w25q128_handle)&0xFFFFFF);
	/* 读取flash ID，验证打开是否在正常	*/
	if(  flash_id != SPI_FLASH_ID)
	{
		w25q128_handle.status=FLASH_ERR;
		w25q128_handle.errcode=FLASH_ERR_OPEN;
		Critical_Log("W25Q read id:0x%06X, error!\r\n",flash_id);
	}
	else
	{
		w25q128_handle.status=FLASH_IDLE;
		w25q128_handle.errcode=FLASH_ERR_NONE;
	}
#if SPI_FLASH_ASYN
	rt_sem_release(&w25q128_handle.w25q_sem);
//	rt_sem_release(&w25q128_handle.dma_sem);
#endif
	return &w25q128_handle;
}
/*flash 读取函数*/
flash_err_t flash_read(w25q128_t* handle,uint32_t addr,uint8_t *buf,uint16_t size)
{
	ASSERT(handle);
	ASSERT(buf);
#if SPI_FLASH_ASYN
	if(RT_EOK != rt_sem_take(&w25q128_handle.w25q_sem,RT_WAITING_FOREVER))
	{
		Critical_Log("Spi flash sem take failed!\r\n");
		return FLASH_ERR_READ;
	}
#endif
	flash_err_t ret = spi_flash_read(handle,addr,buf,size);

#if SPI_FLASH_ASYN
	rt_sem_release(&w25q128_handle.w25q_sem);
#endif
	return ret;
	
}
/*******************************************************************************
 *
* @brief   flash_write   flash 写入比较麻烦，需要 读取 擦除 写入配合，这个写入函数统筹了不同写入方式，
 *
 * @param
 *
 * @return
 *
*******************************************************************************/
flash_err_t flash_write(w25q128_t* handle,uint32_t addr,uint8_t *buf,uint16_t size,uint8_t write_method)
{
	ASSERT(handle);
	ASSERT(buf);
#if SPI_FLASH_ASYN
	if(RT_EOK != rt_sem_take(&w25q128_handle.w25q_sem,RT_WAITING_FOREVER))
	{
		Critical_Log("Spi flash sem take failed!\r\n");
		return FLASH_ERR_READ;
	}
#endif
	uint16_t sector_left;
	uint8_t * p_write;
	uint16_t	write_size;
	uint32_t 	write_addr;
	
	uint8_t * p_next=buf;
	uint32_t	flash_addr=addr;
	uint16_t	remain_size=size;

	while(remain_size)
	{
		switch(write_method){
			case FLASH_WRITE_NORMAL:
				spi_flash_read(handle,flash_addr&W25X_SECTOR_MSK,flash_cache,W25X_SECTOR_SIZE);
			
				p_write=flash_cache;
				write_size=W25X_SECTOR_SIZE;
				write_addr=flash_addr&W25X_SECTOR_MSK;
			
				sector_left=W25X_SECTOR_SIZE - (flash_addr&(W25X_SECTOR_SIZE-1));
				/*当前sector剩余量 小于剩余待写入量*/
				if(sector_left < remain_size)
				{
					memcpy(flash_cache+(flash_addr&(W25X_SECTOR_SIZE-1)),p_next,sector_left);
					
					p_next+=sector_left;
					flash_addr+=sector_left;
					remain_size-=sector_left;
				}
				else
				{
					memcpy(flash_cache+(flash_addr&(W25X_SECTOR_SIZE-1)),p_next,remain_size);
					
					p_next+=remain_size;
					flash_addr+=remain_size;
					remain_size=0;
				}
				spi_flash_erase(handle,flash_addr&W25X_SECTOR_MSK,W25X_SECTOR_SIZE);
				break;
			case FLASH_WRITE_ERASE:
				spi_flash_erase(handle,flash_addr&W25X_SECTOR_MSK,W25X_SECTOR_SIZE);
			case FLASH_WRITE_THROUGH:
				sector_left=W25X_SECTOR_SIZE - (flash_addr&(W25X_SECTOR_SIZE-1));
				
				p_write=p_next;
				write_addr=flash_addr;
				if(sector_left < remain_size)
				{
					write_size=sector_left;
					
					p_next+=sector_left;
					flash_addr+=sector_left;
					remain_size-=sector_left;
				}
				else
				{
					write_size=remain_size;
					
					p_next+=remain_size;
					flash_addr+=remain_size;
					remain_size=0;
				}
				break;
			default:return FLASH_ERR_WRITE;
		}
		flash_sector_write(handle,write_addr,p_write,write_size);
	}
	
#if SPI_FLASH_ASYN
	rt_sem_release(&w25q128_handle.w25q_sem);
#endif
	return FLASH_ERR_NONE;
}
/*擦除整个sector*/
flash_err_t flash_erase(w25q128_t* handle,uint32_t addr,uint32_t size)
{
	ASSERT(handle);
#if SPI_FLASH_ASYN
	if(RT_EOK != rt_sem_take(&w25q128_handle.w25q_sem,RT_WAITING_FOREVER))
	{
		Critical_Log("Spi flash sem take failed!\r\n");
		return FLASH_ERR_READ;
	}
#endif
	flash_err_t ret = spi_flash_erase(handle,addr,size);

#if SPI_FLASH_ASYN
	rt_sem_release(&w25q128_handle.w25q_sem);
#endif
	return ret;
	
}

flash_err_t flash_chip_erase(w25q128_t* handle)
{
	ASSERT(handle);
#if SPI_FLASH_ASYN
	if(RT_EOK != rt_sem_take(&w25q128_handle.w25q_sem,RT_WAITING_FOREVER))
	{
		Critical_Log("Spi flash sem take failed!\r\n");
		return FLASH_ERR_READ;
	}
#endif
	flash_write_enable(handle);

	flash_send_cmd(handle,W25X_ChipErase);

	flash_wait_busy(handle);
#if SPI_FLASH_ASYN
	rt_sem_release(&w25q128_handle.w25q_sem);
#endif
	return FLASH_ERR_NONE;
}

static void flash_send_cmd(w25q128_t* handle,uint8_t cmd)
{
	__W25Q_CS_SELECT(handle);
	spi_transfer_byte(handle->spi_handle, cmd);
	__W25Q_CS_DESELECT(handle);
}

/*******************************************************************************
 *
 * @brief    flash_lock  和flash_unlockf配对使用，同一时间只能有一个任务调用flash，
 *						 所以应用程序在操作flash之前必须手动上锁
*******************************************************************************/
//static flash_err_t flash_lock(w25q128_t* handle)
//{
//	flash_err_t ret=FLASH_ERR_NONE;
//	return ret;
//}
/*******************************************************************************
 *
 * @brief    flash_unlock  和flash_lock配对使用，
 *
 * @param
 *
 * @return    无
 *
*******************************************************************************/
//static flash_err_t flash_unlock(w25q128_t* handle)
//{
//	return FLASH_ERR_NONE;
//}
static flash_err_t flash_sector_erase(w25q128_t* handle,uint32_t addr)
{
	addr &= W25X_SECTOR_MSK;

	flash_write_enable(handle);
	
	__W25Q_CS_SELECT(handle);
	spi_transfer_byte(handle->spi_handle, W25X_SectorErase);
	spi_transfer_byte(handle->spi_handle, (addr>>16)&0xFF);
	spi_transfer_byte(handle->spi_handle, (addr>>8)&0xFF);
	spi_transfer_byte(handle->spi_handle, addr&0xFF);
	__W25Q_CS_DESELECT(handle);

	flash_wait_busy(handle);
	flash_write_disable(handle);

	return FLASH_ERR_NONE;
}

/*******************************************************************************
 *
 * @brief   flash_read_id  	 读flash的ID
 *
 * @param   无
 *
 * @return  flash id
 *
*******************************************************************************/
uint32_t flash_read_id(w25q128_t* handle)
{
	uint32_t temp = 0;
	uint32_t res  = 0;
	/* 查看设备是否空闲 */
	flash_wait_busy(handle);
	
	uint8_t sendbuff[] = {W25X_JedecDeviceID,0xff,0xff,0xff};
	uint8_t rtn[4];
	__W25Q_CS_SELECT(handle);
	spi_transfer_receive(handle->spi_handle,sendbuff,rtn,sizeof(sendbuff));
	__W25Q_CS_SELECT(handle);
	temp = (rtn[1]<<16) + (rtn[2]<<8) + rtn[3];
	return temp;
}
/*
在datasheet里说 在写入 和 擦除 操作完成 write enable都会复位,需要手动置位
在status寄存器中的wel位为1后才能进行写操作
*/
static void flash_write_enable(w25q128_t* handle)
{
	flash_wait_busy(handle);
	flash_send_cmd(handle,W25X_WriteEnable);
	wait_write_enable(handle);
}

static void flash_write_disable(w25q128_t* handle)
{
	flash_wait_busy(handle);
	flash_send_cmd(handle,W25X_WriteDisable);
	wait_write_disable(handle);
}

/*******************************************************************************
 *
 * @brief   flash_page_write  写函数,再同一个sector中 默认已经擦除
 *
 * @param    addr
 *			 buf
 *           size
 *
 * @return
 *
*******************************************************************************/
static flash_err_t flash_sector_write(w25q128_t* handle,uint32_t addr,uint8_t *buf,uint16_t size)
{
	uint32_t remain=size;
	uint8_t* p_next=buf;
	uint32_t flash_addr=addr;
	
	uint16_t page_left;
	if(NULL == handle || addr>(W25X_TOTAL_SIZE-1) || buf == NULL)
		return FLASH_ERR_WRITE;
	
	while(remain){
		page_left=W25X_PAGE_SIZE-flash_addr%W25X_PAGE_SIZE;
		if(page_left<remain)
		{
			flash_page_write(handle,flash_addr,p_next,page_left);
			flash_addr+=page_left;
			p_next+=page_left;
			remain-=page_left;
		}
		else
		{
			flash_page_write(handle,flash_addr,p_next,remain);
			remain=0;
		}
	}
//	for(cnt=0;cnt<(W25X_SECTOR_SIZE/W25X_PAGE_SIZE);cnt++)
//	{
//		flash_page_write(handle,(addr&W25X_SECTOR_MSK)+(cnt*W25X_PAGE_SIZE),buf+cnt*W25X_PAGE_SIZE,W25X_PAGE_SIZE);
//	}
	return FLASH_ERR_NONE;
}
/*******************************************************************************
 *
 * @brief   flash_page_write  写函数，一次最大写入256字节，如果地址不是256字节对齐
 *                            则写入当前地址到下一个256字节对齐处
 *
 * @param    addr
 *			 buf
 *           size
 *
 * @return
 *
*******************************************************************************/
static flash_err_t flash_page_write(w25q128_t* handle,uint32_t addr,uint8_t *buf,uint16_t size)
{
	uint16_t len;

	/* 地址对256字节去模运算 */
	len = addr % W25X_PAGE_SIZE;

	/* 计算本次写入地址到下一个页对齐处的长度，即为一次写入的长度 */
	len = W25X_PAGE_SIZE - len;

	if( size  <= len )
	{
		len = size;
	}

	flash_write_enable(handle);

	__W25Q_CS_SELECT(handle);
	spi_transfer_byte(handle->spi_handle, W25X_PageProgram);
	spi_transfer_byte(handle->spi_handle, (addr>>16)&0xFF);
	spi_transfer_byte(handle->spi_handle, (addr>>8)&0xFF);
	spi_transfer_byte(handle->spi_handle, addr&0xFF);
#if SPI_FLASH_ASYN	
//	if(RT_EOK != rt_sem_take(&w25q128_handle.dma_sem,RT_WAITING_FOREVER))
//	{
//		Critical_Log("Spi flash dma sem take failed!\r\n");
//		return FLASH_ERR_READ;
//	}
//	spi_dma_transfer(w25q128_handle.spi_handle,buf,buf,size);
//	/*等待spi dma完成中断中释放dma_sem*/
//	if(RT_EOK != rt_sem_take(&w25q128_handle.dma_sem,RT_WAITING_FOREVER))
//	{
//		Critical_Log("Spi flash dma sem take failed!\r\n");
//		return FLASH_ERR_READ;
//	}
//	else
//		rt_sem_release(&w25q128_handle.dma_sem);
	spi_write(handle->spi_handle, buf,len);
#else
	spi_write(handle->spi_handle, buf,len);
#endif
	__W25Q_CS_DESELECT(handle);
	flash_wait_busy(handle);
	
	flash_write_disable(handle);
	
	return FLASH_ERR_NONE;
}

/*******************************************************************************
 *
 * @brief   spi_flash_wait_busy  等待写结束
 *
 * @param
 *
 * @return
 *
*******************************************************************************/
static void flash_wait_busy(w25q128_t* handle)
{
	__W25Q_CS_SELECT(handle);
	volatile uint8_t status = 1;
	while(status & 0x1)
	{
		status = spi_transfer_byte(handle->spi_handle, W25X_ReadStatusReg);
		status = spi_transfer_byte(handle->spi_handle, 0xFF);
#if SPI_FLASH_ASYN
//		rt_thread_delay(5);
#endif
	}
	__W25Q_CS_DESELECT(handle);
}

/*******************************************************************************
 *
 * @brief   spi_flash_write_enable  等待写结束
 *
 * @param
 *
 * @return
 *
*******************************************************************************/
static void wait_write_enable(w25q128_t* handle)
{
	__W25Q_CS_SELECT(handle);
	volatile uint8_t status = 0;
	while((status & 0x2) == 0)
	{
		status = spi_transfer_byte(handle->spi_handle, W25X_ReadStatusReg);
		status = spi_transfer_byte(handle->spi_handle, 0xFF);
#if SPI_FLASH_ASYN
//		rt_thread_delay(5);
#endif
	}
	__W25Q_CS_DESELECT(handle);
	
//	volatile uint8_t status = 0;
//	__W25Q_CS_SELECT(handle);
//	do
//	{
//		spi_transfer_byte(handle->spi_handle, W25X_ReadStatusReg);
//		status = spi_transfer_byte(handle->spi_handle, 0xFF);
//	}
//	while ( (status & 0x2) == 0 ); 
//	__W25Q_CS_DESELECT(handle);
}


static void wait_write_disable(w25q128_t* handle)
{
	__W25Q_CS_SELECT(handle);
	volatile uint8_t status = 2;
	while((status & 0x2))
	{
		status = spi_transfer_byte(handle->spi_handle, W25X_ReadStatusReg);
		status = spi_transfer_byte(handle->spi_handle, 0xFF);
#if SPI_FLASH_ASYN
//		rt_thread_delay(5);
#endif
	}
	__W25Q_CS_DESELECT(handle);
//	
//	volatile uint8_t status = 0;
//	__W25Q_CS_SELECT(handle);
//	do
//	{
//		spi_transfer_byte(handle->spi_handle, W25X_ReadStatusReg);
//		status = spi_transfer_byte(handle->spi_handle, 0xFF);
//	}
//	while ( status & 0x2 ); 	
//	__W25Q_CS_DESELECT(handle);
}
static void spi_dma_cplt_callback(spi_handle_t * handle,uint8_t * rxbuff, uint16_t size)
{
#if SPI_FLASH_ASYN
//	if(handle == w25q128_handle.spi_handle)
//	{
//		rt_sem_release(&w25q128_handle.dma_sem);
//	}
#endif
}
static flash_err_t spi_flash_read(w25q128_t* handle ,uint32_t addr,uint8_t *buf,uint16_t size)
{
	flash_err_t ret = FLASH_ERR_NONE;
	int res;
	/* 查看flash状态 */
	flash_wait_busy(handle);

	__W25Q_CS_SELECT(handle);
	spi_transfer_byte(handle->spi_handle, W25X_ReadData);
	spi_transfer_byte(handle->spi_handle, (addr>>16)&0xFF);
	spi_transfer_byte(handle->spi_handle, (addr>>8)&0xFF);
	spi_transfer_byte(handle->spi_handle, addr&0xFF);

	memset(buf,0xff,size);
#if SPI_FLASH_ASYN
//	if(RT_EOK != rt_sem_take(&w25q128_handle.dma_sem,RT_WAITING_FOREVER))
//	{
//		Critical_Log("Spi flash dma sem take failed!\r\n");
//		return FLASH_ERR_READ;
//	}
//	spi_dma_transfer(w25q128_handle.spi_handle,buf,buf,size);
//	/*等待spi dma完成中断中释放dma_sem*/
//	if(RT_EOK != rt_sem_take(&w25q128_handle.dma_sem,RT_WAITING_FOREVER))
//	{
//		Critical_Log("Spi flash dma sem take failed!\r\n");
//		return FLASH_ERR_READ;
//	}
//	else
//		rt_sem_release(&w25q128_handle.dma_sem);
	res = spi_read(handle->spi_handle, buf, size);
#else
	res = spi_read(handle->spi_handle, buf, size);
#endif
	if( res != SPI_ERR_NONE)
	{		
		ret = FLASH_ERR_READ;
	}
	__W25Q_CS_DESELECT(handle);
	return ret;
}

static flash_err_t spi_flash_erase(w25q128_t* handle,uint32_t addr,uint32_t size)
{
	uint32_t last_sector=(addr+size)&W25X_SECTOR_MSK;
	uint32_t current_sector=addr&W25X_SECTOR_MSK;
	while(current_sector < last_sector){
		flash_sector_erase(handle,current_sector);
		current_sector+=W25X_SECTOR_SIZE;
	}
	return FLASH_ERR_NONE;
}